Skip to content

S03-02 核心类-包装类

[TOC]

包装类

在 Java 中,包装类(Wrapper Class) 是一个非常基础且核心的概念。Java 是一种面向对象的语言,但为了性能考量,它保留了 8 种基本数据类型(如 int, double 等),这些基本类型并不是对象。

为了让基本数据类型也能拥有对象的特征(例如参与面向对象的操作调用方法放入集合中),Java 为每一种基本数据类型都提供了一个对应的包装类

基本数据类型 vs 包装类

基本数据类型与包装类的对应关系:

Java 的 java.lang 包中包含了 8 种基本数据类型的包装类。它们的对应关系非常简单,除了 intchar 之外,其他的只需将首字母大写即可:

基本数据类型 (Primitive Type)包装类 (Wrapper Class)父类
byteByteNumber
shortShortNumber
intIntegerNumber
longLongNumber
floatFloatNumber
doubleDoubleNumber
charCharacterObject
booleanBooleanObject

为什么需要包装类

为什么需要包装类:

既然有了基本数据类型,为什么还要多此一举设计包装类呢?主要有以下几个原因:

  1. 集合框架(Collections)的限制:Java 中的集合(如 List, Set, Map)只能存储对象,不能直接存储基本数据类型。如果你想创建一个包含整数的列表,只能写 List<Integer>,而不能写 List<int>

  2. 表示“空值”(null):基本数据类型有默认值(如 int 默认是 0),但无法表示“没有值”的状态。而在实际开发(尤其是数据库操作和 Web 请求)中,0 和 null 的意义完全不同。包装类作为对象,可以被赋值为 null

  3. 提供丰富的实用方法和常量:包装类中封装了大量有用的方法。例如,Integer.parseInt("123") 可以将字符串转为整数,Integer.MAX_VALUE 可以直接获取 int 类型的最大值。

自动装箱与自动拆箱

自动装箱(Autoboxing)与自动拆箱(Unboxing):

在 Java 5 之前,基本类型和包装类之间的转换需要手动进行,代码显得非常繁琐。从 Java 5 开始,引入了自动装箱自动拆箱机制。

  • 自动装箱:Java 编译器自动将基本数据类型转换为对应的包装类

    • 底层原理:编译器在编译时调用了包装类的 valueOf() 方法。
  • 自动拆箱:Java 编译器自动将包装类转换为对应的基本数据类型

    • 底层原理:编译器在编译时调用了包装类的 Number 超类的 xxxValue() 方法(如 intValue())。

示例

  1. Java 5 之前的写法(手动)

    java
    // Java 5 之前的写法(手动)
    Integer numObj = Integer.valueOf(100); // 装箱
    int num = numObj.intValue();           // 拆箱
  2. Java 5 之后的写法(自动装箱与拆箱)

    java
    // Java 5 之后的写法(自动装箱与拆箱)
    Integer a = 10;  // 自动装箱:等同于 Integer.valueOf(10);
    int b = a;       // 自动拆箱:等同于 a.intValue();
    
    // 甚至可以在数学运算中自动混用
    Integer x = 5; 
    Integer y = x + 5; // 先把 x 拆箱变成 5,相加得到 10,再把 10 自动装箱赋值给 y

内存结构变化

转换成包装类后,内存结构有如下变化

java
public static void main(String[] args) {
  int num = 520;
  Integer obj = new Integer(520);
}

image-20260317165958118

常量池缓存

包装类的常量池缓存机制(面试高频考点):

为了提高性能并减少内存空间的占用,Java 在 Byte, Short, Integer, Long, Character, Boolean 这几个包装类中实现了缓存机制(Cache)

以最常用的 Integer 为例,Java 内部维护了一个 IntegerCache,默认缓存了 -128 到 127 之间的 Integer 对象。

当你通过自动装箱(即 Integer.valueOf(i))创建一个在该范围内的对象时,Java 会直接从缓存池中返回已经存在的对象,而不会 new 一个新对象;如果超出这个范围,则会去堆内存中 new 一个新对象。

java
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // true (都在 -128 ~ 127 范围内,指向缓存池中的同一个对象)

Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false (超出了范围,底层每次都 new 了一个新对象,内存地址不同)

注意DoubleFloat 并没有实现缓存机制,因为在任何范围内,浮点数都有无限多个。

包装类比较

包装类比较时的注意事项:

因为包装类是对象,所以在进行相等性比较时,必须非常小心:

  1. 比较值是否相等,必须使用 .equals() 方法,千万不要使用 ==(除非你在比较基本数据类型)。== 比较的是对象的内存地址,而 .equals() 被重写过,比较的是对象内部包裹的值。

    java
    Integer i1 = new Integer(10);
    Integer i2 = new Integer(10);
    System.out.println(i1 == i2);      // false (new 出来的对象地址一定不同)
    System.out.println(i1.equals(i2)); // true (值相等)
  2. 基本类型与包装类用 == 比较时,包装类会自动拆箱

    java
    int num1 = 200;
    Integer num2 = 200;
    System.out.println(num1 == num2); // true (num2 会自动拆箱为 int,然后比较数值)

常见类型转换

常见类型转换操作:

包装类在字符串与基本类型之间的转换中扮演着重要角色:

  1. 字符串转基本类型

    使用包装类的 parseXxx(String s) 方法(最常用):

    java
    int i = Integer.parseInt("123");
    double d = Double.parseDouble("3.14");
  2. 字符串转包装类对象

    使用 valueOf(String s) 方法:

    java
    Integer num = Integer.valueOf("123");
  3. 基本类型转字符串

    除了直接用 "" + 123 这种方式外,还可以用 String.valueOf()

    java
    String s1 = String.valueOf(123); // "123"

image-20260324152747528

自定义包装类

image-20260317170309954

练习题

练习题

利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。

提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。

1、创建Vector对象:Vector v=new Vector();

2、给向量添加元素:v.addElement(Object obj); //obj必须是对象

3、取出向量中的元素:Object obj=v.elementAt(0);
注意第一个元素的下标是0,返回值是Object类型的。

4、计算向量的长度:v.size();

5、若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等

Integer

在 Java 日常开发中,Integer 是使用频率极高的包装类。除了作为 int 的对象形态存在,它内部还封装了大量极其好用的静态方法和常量,涵盖了类型转换、进制转换、数值比较和位运算等场景。

常量

核心常量:

Integer 类提供了一些常量,可以让你直接获取 int 类型的物理属性,23112^{31}-1 避免硬编码:

常量名称描述实际值
Integer.MAX_VALUEint 类型能表示的最大值2147483647 (23112^{31}-1)
Integer.MIN_VALUEint 类型能表示的最小值-2147483648 (231-2^{31})
Integer.SIZEint 值在内存中占用的位数(Bit)32
Integer.BYTESint 值在内存中占用的字节数(Byte)4 (Java 8 引入)

(提示:在求数组最大值/最小值算法的初始化时,经常会用到 MIN_VALUEMAX_VALUE。)

方法

字符串转数字

这是日常开发中最频繁的操作,主要处理前端传来的字符串数据或进行日志打印。

parseInt()
  • static int parseInt (String s):将字符串解析为基本数据类型 int。如果字符串不是合法的数字(例如包含字母),会抛出 NumberFormatException

    java
    int num = Integer.parseInt("1024");
  • static int parseInt (String s, int radix):将指定进制的字符串解析为十进制的 int

    java
    int num = Integer.parseInt("FF", 16); // 将 16 进制的 FF 转为 10 进制,结果是 255
valueOf()
  • static Integer valueOf (String s):将字符串解析为包装类 Integer 对象。底层其实就是调用了 parseInt,然后再进行装箱。

数字转字符串

toString()
  • static String toString (int i):将基本类型 int 转换为字符串。

    java
    String s = Integer.toString(100); // 相当于 String.valueOf(100)

进制转换

如果你需要将一个十进制整数打印成二进制、八进制或十六进制字符串,Integer 提供了直接的静态方法,无需自己写取余算法:

toBinaryString()
  • static String toBinaryString (int i):转为二进制字符串。
toOctalString()
  • static String toOctalString (int i):转为八进制字符串。
toHexString()
  • static String toHexString (int i):转为十六进制字符串。

    java
    int num = 28;
    System.out.println(Integer.toBinaryString(num)); // "11100"
    System.out.println(Integer.toHexString(num));    // "1c"

对象的创建与缓存

valueOf()
  • static Integer valueOf (int i):这是目前官方唯一推荐的创建 Integer 对象的方式。

    避坑指南:自 Java 9 起,构造方法 new Integer(10) 已被标记为废弃(Deprecated)。因为 valueOf 方法内部使用了前面提到的 -128 到 127 的缓存池机制,性能更好,内存占用更低。

大小比较

compareTo()
  • int compareTo (Integer anotherInteger):实例方法。比较两个 Integer 对象的值。返回 0(相等)、< 0(小于)、> 0(大于)。
compare()
  • static int compare (int x, int y):静态方法。直接比较两个基本类型 int 的大小。返回值同上。

    java
    System.out.println(Integer.compare(10, 20)); // 返回 -1

数学运算@JDK8

为了配合 Lambda 表达式和 Stream API(如 reduce 操作),Java 8 在 Integer 中新增了几个静态算术方法:

max()
  • static int max (int a, int b):返回两者中较大的值。
min()
  • static int min (int a, int b):返回两者中较小的值。
sum()
  • static int sum (int a, int b):返回两数之和

底层位运算

在一些追求极致性能的算法、源码或密码学领域,经常会用到 Integer 提供的位级操作方法:

bitCount()
  • static int bitCount (int i):返回指定 int 值的二进制补码表示中,1 的个数。(LeetCode 常考题)。

    java
    // 7 的二进制是 0000 0111,里面有 3 个 1
    System.out.println(Integer.bitCount(7)); // 输出: 3
reverse()
  • static int reverse (int i):将二进制位的顺序完全反转。
highestOneBit()
  • static int highestOneBit (int i):保留最高位的 1,其余全置为 0(常用于 HashMap 容量计算的底层逻辑中)。

总结避坑

当你在业务代码中比较两个 Integer 对象的值是否相等时,永远使用 .equals() 方法,绝不要使用 ==。这是因为缓存池(-128 ~ 127)的存在会让 == 在小数值时表现正常,一旦超过这个范围,== 就会因为内存地址不同而返回 false,引发难以排查的 Bug。

Character

在 Java 中,Character 是基本数据类型 char 的包装类。由于 char 在 Java 中占用 2 个字节(16 位)本质上是一个无符号的整数(对应 Unicode 字符集),因此 Character 类不仅提供了对象化的包装,还封装了大量用于字符分类、判断和转换的实用静态方法。

方法

字符类型判断

isXxx()

字符类型判断(最常用):

在处理字符串解析、表单验证或算法题(如验证回文串)时,我们经常需要判断一个字符是字母、数字还是空格。Character 提供了一系列 isXxx() 方法:

方法声明(static int)功能描述示例与返回值
isDigit(char ch)判断是否为数字 (0-9)isDigit('5') \rightarrow true
isLetter(char ch)判断是否为字母 (包含中文字符等 Unicode 字母)isLetter('A') \rightarrow true
isLetter('中') \rightarrow true
isLetterOrDigit(char ch)判断是否为字母或数字isLetterOrDigit('!') \rightarrow false
isWhitespace(char ch)判断是否为空白字符(空格、Tab \t、换行 \n 等)isWhitespace(' ') \rightarrow true
isUpperCase(char ch)判断是否为大写字母isUpperCase('a') \rightarrow false
isLowerCase(char ch)判断是否为小写字母isLowerCase('A') \rightarrow false

注意isLetter 不仅仅识别 26 个英文字母,它基于 Unicode 标准,因此判断汉字等其他语言的字母字符时也会返回 true

大小写转换

大小写转换:

如果需要统一字符的格式,可以使用以下两个转换方法。如果传入的字符本身不是字母,或者已经是要转换的大小写,方法会直接返回原字符:

toUpperCase()
  • static char toUpperCase(char ch):将字符转换为大写
toLowerCase()
  • static char toLowerCase(char ch):将字符转换为小写
java
char c1 = Character.toUpperCase('a'); // 返回 'A'
char c2 = Character.toLowerCase('B'); // 返回 'b'
char c3 = Character.toUpperCase('5'); // 返回 '5' (非字母不改变)

字符与数字的转换

字符与数字的转换(避坑指南):

这是一个非常常见的易错点:如果你有一个字符 '9',直接强制转换为 int,得到的是它的 ASCII 码值 57,而不是数字 9

getNumericValue()
  • static int getNumericValue(char ch):获取字符对应的真实数值。返回指定字符表示的 int 值。

    java
    char ch = '9';
    System.out.println((int) ch); // 输出 57 (这是 ASCII 码)
    System.out.println(Character.getNumericValue(ch)); // 输出 9 (这是真实的数值)

    进阶小技巧(减去 '0'):

    在算法中,更高效且常见的做法是直接利用字符的 ASCII 码差值来获取数字:

    java
    char ch = '7';
    int num = ch - '0'; // 底层是 55 - 48,结果就是 7。比调用方法更快!

缓存机制与对象创建

Character 的缓存机制与对象创建:

Integer 类似,Character 也有为了节省内存而设计的常量池缓存机制

  • 缓存范围Character 缓存了从 \u0000\u007F(即 0 到 127)的字符,这刚好覆盖了所有标准的 ASCII 字符

  • 推荐创建方式Character.valueOf(char c)

    自 Java 9 起,new Character('A') 同样被废弃,官方推荐使用 valueOf()

java
Character c1 = Character.valueOf('A'); // ASCII 为 65
Character c2 = Character.valueOf('A');
System.out.println(c1 == c2); // true (命中 0-127 的缓存池,地址相同)

Character c3 = Character.valueOf('中'); // Unicode 远超 127
Character c4 = Character.valueOf('中');
System.out.println(c3 == c4); // false (超出缓存范围,创建了新对象)
System.out.println(c3.equals(c4)); // true (值相等)

处理复杂字符

处理复杂字符(Emoji 与补充字符):

这是 Character 类较深的应用场景。标准的 char 是 16 位的,只能表示 Unicode 的基本多语言面(BMP)。但是,像 Emoji 表情(如 😭)或者某些生僻字的 Unicode 编码超过了 16 位,一个 char 是存不下的。

因此,Java 使用两个 char(称为代理对,Surrogate Pair) 来表示这些复杂的字符。

为了处理这种情况,Character 提供了支持传入 int codePoint(代码点,即真实的 Unicode 编码数值)的重载方法:

  • static boolean isSupplementaryCodePoint(int codePoint):判断是否为增补字符(如 Emoji)。
  • static int charCount(int codePoint):判断该字符需要几个 char 来表示(通常是 1 或 2)。

分隔-------------------------------

案例演示(Integer01.java)

java
package com.hspedu.wrapper;

public class Integer01 {
    public static void main(String[] args) {
        // JDK5前:手动装箱/拆箱
        // 手动装箱
        int n1 = 100;
        Integer integer = new Integer(n1);
        Integer integer1 = Integer.valueOf(n1);

        // 手动拆箱
        int i = integer.intValue();

        // JDK5后:自动装箱/拆箱
        int n2 = 200;
        Integer integer2 = n2; // 自动装箱(底层Integer.valueOf(n2))
        int n3 = integer2; // 自动拆箱(底层integer2.intValue())
    }
}

包装类型和 String 类型的相互转换

java
package com.hspedu.wrapper;

public class WrapperVSString {
    public static void main(String[] args) {
        // 包装类 -> String
        Integer i = 100;
        String str1 = i + ""; // 方式1
        String str2 = i.toString(); // 方式2
        String str3 = String.valueOf(i); // 方式3

        // String -> 包装类
        String str4 = "12345";
        Integer i2 = Integer.parseInt(str4); // 方式1(推荐)
        Integer i3 = new Integer(str4); // 方式2(不推荐)
    }
}

Integer 和 Character 类的常用方法

java
package com.hspedu.wrapper;

public class WrapperMethod {
    public static void main(String[] args) {
        // Integer常用方法
        System.out.println(Integer.MIN_VALUE); // 最小值
        System.out.println(Integer.MAX_VALUE); // 最大值

        // Character常用方法
        System.out.println(Character.toUpperCase('a')); // 转大写
        System.out.println(Character.isDigit('a')); // 是否为数字
        System.out.println(Character.isLetter('a')); // 是否为字母
        System.out.println(Character.isUpperCase('a')); // 是否为大写
        System.out.println(Character.isLowerCase('a')); // 是否为小写
        System.out.println(Character.isWhitespace(' ')); // 是否为空格
    }
}

Integer 类面试题

java
package com.hspedu.wrapper;

public class WrapperExercise02 {
    public static void main(String[] args) {
        Integer i = new Integer(1);
        Integer j = new Integer(1);
        System.out.println(i == j); // False(不同对象)

        Integer m = 1; // 底层Integer.valueOf(1)
        Integer n = 1;
        System.out.println(m == n); // True(-128~127缓存)

        Integer x = 128;
        Integer y = 128;
        System.out.println(x == y); // False(超出缓存范围)
    }
}

缓存机制说明

  • IntegerCache 缓存-128~127 之间的整数
  • 超出范围时,直接 new Integer 对象

String 类

String 类的核心特性

  1. String 对象用于保存字符串(字符序列)
  2. 字符串常量用双引号括起(如"hello")
  3. 字符编码:Unicode(每个字符占 2 字节)
  4. 实现接口:Serializable(可串行化)、Comparable(可比较)
  5. String 是 final 类,不能被继承
  6. 底层存储:private final char value[](value 数组不可变)

创建 String 对象的两种方式

  1. 直接赋值String s = "hspedu";
    • 先从常量池查找,有则直接指向,无则创建后指向
  2. 调用构造器String s = new String("hspedu");
    • 先在堆中创建对象,再指向常量池的字符串

案例演示(String01.java)

java
package com.hspedu.string_;

public class String01 {
    public static void main(String[] args) {
        // 构造器创建
        String s1 = new String();
        String s2 = new String("hspedu");
        char[] chars = {'h','s','p','e','d','u'};
        String s3 = new String(chars);
        String s4 = new String(chars, 0, 3); // 从索引0开始,取3个字符

        // 直接赋值
        String s5 = "hspedu";
    }
}

课堂测试题

java
package com.hspedu.string_;

public class StringExercise01 {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        System.out.println(a.equals(b)); // True(内容相同)
        System.out.println(a == b); // True(指向同一常量池对象)
    }
}

测试题 2 StringExercise02.java

java
String a = new String("abc");
String b = new String("abc");
System.out.println(a.equals(b));// T
System.out.println(a == b); // F

测试题 3 StringExercise03.java(2min 思考)

java
String a = "hsp";// a指向常量池的"hsp"
String b = new String("hsp");// b指向堆中对象
System.out.println(a.equals(b));// T
System.out.println(a == b.intern());// T //intern方法自己先查看API
System.out.println(b == b.intern()); // F
System.out.println(a == b); // F

知识点:

当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object)方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。

老韩解读: (1) b.intern()方法最终返回的是常量池的地址(对象)

对应的示意图: 第 544 页

测试题 4 StringExercise04.java(2min)

java
String s1 = "hspedu"; // 指向常量池"hspedu"
String s2 = "java";// 指向常量池"java"
String s3 = new String("java");// 指向堆中对象
String s4 = "java";// 指向常量池"java"
System.out.println(s2 == s3);// F
System.out.println(s2 == s4); // T
System.out.println(s2.equals(s3));// T
System.out.println(s1 == s2); // F

第 545 页

测试题 5 StringExercise05.java(5min 思考)

java
Person p1 = new Person();
p1.name = "hspedu";
Person p2 = new Person();
p2.name = "hspedu";
System.out.println(p1.name.equals(p2.name));// 比较内容:True
System.out.println(p1.name == p2.name);// T
System.out.println(p1.name == "hspedu"); // T

String s1 = new String("bcde");
String s2 = new String("bcde");
System.out.println(s1 == s2); // False
// 请画出内存布局图

13.3 字符串的特性

说明 StringExercise06.java

  1. String 是一个 final 类,代表不可变的字符序列
  2. 字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
java
String s1 = "hello";
s1 = "haha";
// 1min 1.以下语句创建了几个对象?画出内存布局图。
// 创建了2个对象.

面试题

  1. 题 1 String a = "hello" + "abc"; 创建了几个对象?(1min)

    • 编译器优化:等价于 String a = "helloabc";
    • 结论:只有 1 个对象。
  2. 题 2 String Exercise08.java(1min 思考)

java
String a = "hello";// 创建a对象
String b = "abc";// 创建b对象
String c = a + b;
// 创建了几个对象?画出内存图?

关键分析:String c = a + b; 底层执行流程

java
StringBuilder sb = new StringBuilder();
sb.append(a);
sb.append(b);
// sb是在堆中,并且append是在原来字符串的基础上追加的

老韩小结

  • 常量相加(如"ab"+"cd"):看常量池
  • 变量相加(如 a + b):在堆中创建 StringBuilder 对象处理
  • 结论:一共创建 3 个对象
  1. 题 3 StringExercise09.java
java
String s1 = "hspedu"; // s1指向池中的"hspedu"
String s2 = "java";// s2指向池中的"java"
String s5 = "hspedujava"; // s5指向池中的 "hspedujava"
String s6 = (s1 + s2).intern();// s6指向池中的 "hspedujava"
System.out.println(s5 == s6);// T
System.out.println(s5.equals(s6));// T

测试题 StringExercise10.java

java
public class Test1{
    String str = new String("hsp");
    final char[] ch ={'j','a','v','a'};
    public void change(String str, char ch[]){
        str = "java";
        ch[0] = 'h';
    }
    public static void main(String[] args) {
        Test1 ex = new Test1();
        ex.change(ex.str, ex.ch);
        System.out.print(ex.str + "and ");
        System.out.println(ex.ch);
    }
}
// 思考,认真看,仔细想3min

运行结果:hsp and hava

13.4 String 类的常见方法

说明

String 类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低,因此 java 设计者还提供了 StringBuilder 和 StringBuffer 来增强 String 的功能,并提高效率。[后面我们还会详细介绍 StringBuilder 和 StringBuffer]

java
// 看看这段代码
String s = new String("");
for(int i=0;i<80000; i++){
    s += "hello";
}

String 类的常见方法一览

方法名说明
equals区分大小写,判断内容是否相等
equalsIgnoreCase忽略大小写的判断内容是否相等
length获取字符的个数,字符串的长度
indexOf获取字符在字符串中第 1 次出现的索引,索引从 0 开始,如果找不到,返回-1
lastIndexOf获取字符在字符串中最后 1 次出现的索引,索引从 0 开始,如找不到,返回-1
substring截取指定范围的子串
trim去前后空格
charAt获取某索引处的字符,注意不能使用 Str[index]这种方式
toUpperCase转换成大写
toLowerCase转换成小写
concat拼接字符串
replace替换字符串中的字符
split分割字符串,对于某些分割字符,需要转义比如|、\等
compareTo比较两个字符串的大小
toCharArray转换成字符数组
format格式字符串,%s 字符串、%c 字符、%d 整型、%.2f 浮点型

方法演示 1:StringMethod01.java

java
package com.hspedu.string_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringMethod01 {
    public static void main(String[] args) {
        // 1. equals 前面已经讲过了. 比较内容是否相同,区分大小写
        String str1 = "hello";
        String str2 = "Hello";
        System.out.println(str1.equals(str2));// false

        // 2.equalsIgnoreCase 忽略大小写的判断内容是否相等
        String username = "johN";
        if ("john".equalsIgnoreCase(username)) {
            System.out.println("Success!");
        } else {
            System.out.println("Failure!");
        }

        // 3.length 获取字符的个数,字符串的长度
        System.out.println("韩顺平".length());// 3

        // 4.indexOf 获取字符在字符串对象中第一次出现的索引,索引从0 开始,如果找不到,返回-1
        String s1 = "wer@terwe@g";
        int index = s1.indexOf('@');
        System.out.println(index);// 3
        System.out.println("weIndex=" + s1.indexOf("we"));// 0

        // 5.lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从0 开始,如果找不到,返回-1
        s1 = "wer@terwe@g@";
        index = s1.lastIndexOf('@');
        System.out.println(index);// 11
        System.out.println("ter 的位置=" + s1.lastIndexOf("ter"));// 4

        // 6.substring 截取指定范围的子串
        String name = "hello,张三";
        // 下面name.substring(6) 从索引6 开始截取后面所有的内容
        System.out.println(name.substring(6));// 张三
        // name.substring(0,5)表示从索引0 开始截取,截取到索引5-1=4 位置
        System.out.println(name.substring(2, 5));// llo
    }
}

方法演示 2:StringMethod02.java

java
package com.hspedu.string_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringMethod02 {
    public static void main(String[] args) {
        // 1.toUpperCase 转换成大写
        String s = "heLLo";
        System.out.println(s.toUpperCase());// HELLO

        // 2.toLowerCase
        System.out.println(s.toLowerCase());// hello

        // 3.concat 拼接字符串
        String s1 = "宝玉";
        s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
        System.out.println(s1);// 宝玉林黛玉薛宝钗together

        // 4.replace 替换字符串中的字符
        s1 = "宝玉and 林黛玉林黛玉林黛玉";
        // 在s1 中,将所有的林黛玉替换成薛宝钗
        // 注意对s1 没有任何影响
        String s11 = s1.replace("宝玉", "jack");
        System.out.println(s1);// 宝玉and 林黛玉林黛玉林黛玉
        System.out.println(s11);// jack and 林黛玉林黛玉林黛玉

        // 5.split 分割字符串, 对于某些分割字符,我们需要转义比如| \\等
        String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
        // 老韩解读:
        // 1. 以, 为标准对poem 进行分割, 返回一个数组
        String[] split = poem.split(",");
        System.out.println("==分割后内容===");
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }

        // 分割文件路径
        poem = "E:\\aaa\\bbb";
        split = poem.split("\\\\");
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }

        // 6.toCharArray 转换成字符数组
        s = "happy";
        char[] chs = s.toCharArray();
        for (int i = 0; i < chs.length; i++) {
            System.out.println(chs[i]);
        }

        // 7.compareTo 比较两个字符串的大小
        // 老韩解读
        // (1) 如果长度相同,并且每个字符也相同,就返回0
        // (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小 就返回 c1 - c2
        // (3) 如果前面的部分都相同,就返回str1.len - str2.len
        String a = "jcck";// len = 4
        String b = "jack";// len = 4
        System.out.println(a.compareTo(b)); // 返回值是'c' - 'a' = 2

        // 8.format 格式字符串
        /* 占位符有:
         * %s 字符串、%c 字符、%d 整型、%.2f 浮点型
         */
        String name = "john";
        int age = 10;
        double score = 56.857;
        char gender = '男';

        // 传统拼接方式
        String info = "我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢我!";
        System.out.println(info);

        // format方式
        String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
        String info2 = String.format(formatStr, name, age, score, gender);
        System.out.println("info2=" + info2);
    }
}

第 554 页

13.5 StringBuffer 类

基本介绍

java.lang.StringBuffer 代表可变的字符序列,可以对字符串内容进行增删。很多方法与 String 相同,但 StringBuffer 是可变长度的。StringBuffer 是一个容器。

java
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
    // A cache of the last value returned by toString. Cleared whenever the StringBuffer is modified.
}

StringBuffer01.java

java
package com.hspedu.stringbuffer_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringBuffer01 {
    public static void main(String[] args) {
        // 老韩解读
        // 1. StringBuffer 的直接父类是AbstractStringBuilder
        // 2. StringBuffer 实现了Serializable, 即StringBuffer 的对象可以串行化
        // 3. 在父类中 AbstractStringBuilder 有属性char[] value,不是final
        // 该value 数组存放字符串内容,存放在堆中的
        // 4. StringBuffer 是一个final 类,不能被继承
        // 5. 因为StringBuffer 字符内容是存在char[] value, 所有在变化(增加/删除)
        // 不用每次都更换地址(即不是每次创建新对象),所以效率高于String
        StringBuffer stringBuffer = new StringBuffer("hello");
    }
}

第 556 页

String VS StringBuffer

特性StringStringBuffer
存储内容字符串常量字符串变量
可变性不可变(private final char value[])可变(char[] value,存放在堆)
效率较低(每次更新更换地址)较高(更新内容不换地址)

String 和 StringBuffer 相互转换

StringAndStringBuffer.java

java
package com.hspedu.stringbuffer_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringAndStringBuffer {
    public static void main(String[] args) {
        // String-->StringBuffer
        String str = "hello tom";

        // 方式1 使用构造器
        // 注意:返回的才是StringBuffer 对象,对str 本身没有影响
        StringBuffer stringBuffer = new StringBuffer(str);

        // 方式2 使用的是append 方法
        StringBuffer stringBuffer1 = new StringBuffer();
        stringBuffer1 = stringBuffer1.append(str);

        // StringBuffer ->String
        StringBuffer stringBuffer3 = new StringBuffer("韩顺平教育");

        // 方式1 使用StringBuffer 提供的toString 方法
        String s = stringBuffer3.toString();

        // 方式2: 使用构造器来搞定
        String s1 = new String(stringBuffer3);
    }
}

第 557 页

StringBuffer 类常见方法

StringBufferMethod.java

java
package com.hspedu.stringbuffer_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringBufferMethod {
    public static void main(String[] args) {
        StringBuffer s = new StringBuffer("hello");

        // 增
        s.append("张三丰");// "hello张三丰"
        s.append(',');// "hello张三丰,"
        s.append("赵敏").append(100).append(true).append(10.5);// "hello张三丰,赵敏100true10.5"
        System.out.println(s);// "hello张三丰,赵敏100true10.5"

        // 删
        /*
         * 删除索引为>=start && <end 处的字符
         * 解读: 删除11~14 的字符[11, 14)
         */
        s.delete(11, 14);
        System.out.println(s);// "hello张三丰,赵敏true10.5"

        // 改
        // 老韩解读,使用周芷若替换索引9-11 的字符[9,11)
        s.replace(9, 11, "周芷若");
        System.out.println(s);// "hello张三丰,周芷若true10.5"

        // 查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
        int indexOf = s.indexOf("张三丰");
        System.out.println(indexOf);// 6

        // 插
        // 老韩解读,在索引为9 的位置插入"赵敏",原来索引为9 的内容自动后移
        s.insert(9, "赵敏");
        System.out.println(s);// "hello张三丰,赵敏周芷若true10.5"

        // 长度
        System.out.println(s.length());// 22
    }
}

第 559 页

StringBuffer 类课堂测试题 1 StringBufferExercise01.java

java
package com.hspedu.stringbuffer_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringBufferExercise01 {
    public static void main(String[] args) {
        String str = null;// ok
        StringBuffer sb = new StringBuffer(); // ok
        sb.append(str);// 需要看源码, 底层调用的是AbstractStringBuilder 的appendNull
        System.out.println(sb.length());// 4
        System.out.println(sb);// null

        // 下面的构造器,会抛出NullPointerException
        StringBuffer sb1 = new StringBuffer(str);// 看底层源码super(str.length() + 16);
        System.out.println(sb1);
    }
}

StringBuffer 类课后练习 2 StringBufferExercise02.java

题目要求

输入商品名称和商品价格,要求打印效果示例:

商品名 商品价格
手机 123,564.59

比如价格 3,456,789.88,要求:价格的小数点前面每三位用逗号隔开,在输出。

思路分析

  1. 定义一个 Scanner 对象,接收用户输入的价格(String)
  2. 希望使用到 StringBuffer 的 insert,需要将 String 转成 StringBuffer
  3. 找到小数点的索引,然后在该位置的前 3 位插入逗号,循环处理

代码实现

java
package com.hspedu.stringbuffer_;

import java.util.Scanner;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringBufferExercise02 {
    public static void main(String[] args) {
        String price = "8123564.59";// 测试数据
        StringBuffer sb = new StringBuffer(price);

        // 循环插入逗号
        for (int i = sb.lastIndexOf(".") - 3; i > 0; i -= 3) {
            sb = sb.insert(i, ",");
        }

        System.out.println(sb);// 8,123,564.59
    }
}

第 562 页

13.6 StringBuilder 类

基本介绍

  1. 一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全)。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
  2. 在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。
java
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
}

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    // The value is used for character storage.
    char[] value;
}

StringBuilder01.java

java
package com.hspedu.stringbuilder_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringBuilder01 {
    public static void main(String[] args) {
        // 老韩解读
        // 1. StringBuilder 继承AbstractStringBuilder 类
        // 2. 实现了Serializable ,说明StringBuilder 对象是可以串行化(对象可以网络传输,可以保存到文件)
        // 3. StringBuilder 是final 类, 不能被继承
        // 4. StringBuilder 对象字符序列仍然是存放在其父类AbstractStringBuilder 的char[] value;
        // 因此,字符序列是堆中
        // 5. StringBuilder 的方法,没有做互斥的处理,即没有synchronized 关键字,因此在单线程的情况下使用
        StringBuilder stringBuilder = new StringBuilder();
    }
}

第 564 页

StringBuilder 常用方法

StringBuilder 和 StringBuffer 均代表可变的字符序列,方法是完全一样的,使用方式相同(参考 StringBuffer)。

String、StringBuffer 和 StringBuilder 的比较

特性StringStringBufferStringBuilder
可变性不可变字符序列可变字符序列可变字符序列
线程安全-安全(有 synchronized)不安全
效率最低中等最高
复用率--

效率测试 StringVsStringBufferVsStringBuilder.java

java
package com.hspedu.stringbuilder_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class StringVsStringBufferVsStringBuilder {
    public static void main(String[] args) {
        long startTime = 0L;
        long endTime = 0L;

        // StringBuffer 测试
        StringBuffer buffer = new StringBuffer("");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 80000; i++) {
            buffer.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer 的执行时间:" + (endTime - startTime));

        // StringBuilder 测试
        StringBuilder builder = new StringBuilder("");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 80000; i++) {
            builder.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder 的执行时间:" + (endTime - startTime));

        // String 测试
        String text = "";
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 80000; i++) {
            text = text + i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("String 的执行时间:" + (endTime - startTime));
    }
}

效率结论:StringBuilder > StringBuffer > String

选择原则

  1. 如果字符串存在大量的修改操作,一般使用 StringBuffer 或 StringBuilder
  2. 如果字符串存在大量的修改操作,并在单线程的情况,使用 StringBuilder
  3. 如果字符串存在大量的修改操作,并在多线程的情况,使用 StringBuffer
  4. 如果字符串很少修改,被多个对象引用,使用 String,比如配置信息等

13.7 Math 类

基本介绍

Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。

方法一览(均为静态方法)

方法说明
abs(double a)返回 double 值的绝对值
abs(float a)返回 float 值的绝对值
abs(int a)返回 int 值的绝对值
abs(long a)返回 long 值的绝对值
acos(double a)返回一个值的反余弦,返回的角度范围在 0.0 到 pi 之间
asin(double a)返回一个值的反正弦,返回的角度范围在-pi/2 到 pi/2 之间
atan(double a)返回一个值的反正切,返回的角度范围在-pi/2 到 pi/2 之间
atan2(double y, double x)将矩形坐标(x,y)转换成极坐标(r,theta),返回所得角 theta
cbrt(double a)返回 double 值的立方根
ceil(double a)向上取整,返回>=该参数的最小整数(转成 double)
floor(double a)向下取整,返回<=该参数的最大整数(转成 double)
pow(double a, double b)返回 a 的 b 次方
round(double a)四舍五入
sqrt(double a)返回 double 值的平方根
random()返回 0 <= x < 1 之间的一个随机小数
max(int a, int b)返回两个 int 值的最大值
min(int a, int b)返回两个 int 值的最小值

Math 类常见方法应用案例

MathMethod.java

java
package com.hspedu.math_;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class MathMethod {
    public static void main(String[] args) {
        // 1.abs 绝对值
        int abs = Math.abs(-9);
        System.out.println(abs);// 9

        // 2.pow 求幂
        double pow = Math.pow(2, 4);// 2 的4 次方
        System.out.println(pow);// 16.0

        // 3.ceil 向上取整
        double ceil = Math.ceil(3.9);
        System.out.println(ceil);// 4.0

        // 4.floor 向下取整
        double floor = Math.floor(4.001);
        System.out.println(floor);// 4.0

        // 5.round 四舍五入
        long round = Math.round(5.51);
        System.out.println(round);// 6

        // 6.sqrt 求开方
        double sqrt = Math.sqrt(9.0);
        System.out.println(sqrt);// 3.0

        // 7.random 求随机数
        // 需求:获取a-b 之间的一个随机整数(a,b 均为整数),比如a=2, b=7
        // 公式: (int)(a + Math.random() * (b - a + 1))
        for (int i = 0; i < 100; i++) {
            System.out.println((int) (2 + Math.random() * (7 - 2 + 1)));
        }

        // 8.max , min 返回最大值和最小值
        int min = Math.min(1, 9);
        int max = Math.max(45, 90);
        System.out.println("min=" + min);
        System.out.println("max=" + max);
    }
}

第 569 页

13.8 Arrays 类

Arrays 类常见方法应用案例

Arrays 里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)。

方法说明
toString(arr)返回数组的字符串形式
sort(arr)自然排序
sort(arr, Comparator)定制排序
binarySearch(arr, key)通过二分搜索法进行查找,要求必须排好序
copyOf(arr, length)数组元素的复制
fill(arr, value)数组元素的填充
equals(arr1, arr2)比较两个数组元素内容是否完全一致
asList(values)将一组值,转换成 list

案例 1:ArraysMethod01.java(toString、sort)

java
package com.hspedu.arrays_;

import java.util.Arrays;
import java.util.Comparator;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class ArraysMethod01 {
    public static void main(String[] args) {
        Integer[] integers = {1, 20, 90};
        // 直接使用Arrays.toString 方法,显示数组
        System.out.println(Arrays.toString(integers));// [1, 20, 90]

        // 演示sort 方法的使用
        Integer arr[] = {1, -1, 7, 0, 89};
        System.out.println("排序前=" + Arrays.toString(arr));

        // 定制排序(从大到小)
        Arrays.sort(arr, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Integer i1 = (Integer) o1;
                Integer i2 = (Integer) o2;
                return i2 - i1;
            }
        });

        System.out.println("排序后=" + Arrays.toString(arr));// [89, 7, 1, 0, -1]
    }
}

案例 2:ArraysSortCustom.java(冒泡+定制排序)

java
package com.hspedu.arrays_;

import java.util.Arrays;
import java.util.Comparator;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class ArraysSortCustom {
    public static void main(String[] args) {
        int[] arr = {1, -1, 8, 0, 20};
        // 使用冒泡+定制排序
        bubble02(arr, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                int i1 = (Integer) o1;
                int i2 = (Integer) o2;
                return i2 - i1;// 从大到小
            }
        });
        System.out.println("==定制排序后的情况==");
        System.out.println(Arrays.toString(arr));
    }

    // 普通冒泡排序(从小到大)
    public static void bubble01(int[] arr) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

    // 冒泡+定制排序
    public static void bubble02(int[] arr, Comparator c) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                // 数组排序由c.compare(arr[j], arr[j + 1])返回的值决定
                if (c.compare(arr[j], arr[j + 1]) > 0) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
}

案例 3:ArraysMethod02.java(binarySearch、copyOf、fill、equals、asList)

java
package com.hspedu.arrays_;

import java.util.Arrays;
import java.util.List;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class ArraysMethod02 {
    public static void main(String[] args) {
        Integer[] arr = {1, 2, 90, 123, 567};

        // 1.binarySearch 二叉查找(数组必须有序)
        int index = Arrays.binarySearch(arr, 567);
        System.out.println("index=" + index);// 4

        // 2.copyOf 数组复制
        Integer[] newArr = Arrays.copyOf(arr, arr.length);
        System.out.println("==拷贝执行完毕后==");
        System.out.println(Arrays.toString(newArr));// [1, 2, 90, 123, 567]

        // 3.fill 数组填充
        Integer[] num = new Integer[]{9, 3, 2};
        Arrays.fill(num, 99);
        System.out.println("==num 数组填充后==");
        System.out.println(Arrays.toString(num));// [99, 99, 99]

        // 4.equals 比较两个数组
        Integer[] arr2 = {1, 2, 90, 123};
        boolean equals = Arrays.equals(arr, arr2);
        System.out.println("equals=" + equals);// false

        // 5.asList 转成list
        List asList = Arrays.asList(2, 3, 4, 5, 6, 1);
        System.out.println("asList=" + asList);
        System.out.println("asList 的运行类型" + asList.getClass());// class java.util.Arrays$ArrayList
    }
}

第 578 页

Arrays 类课堂练习 ArrayExercise.java

题目要求

自定义 Book 类,里面包含 name 和 price,按 price 排序(从大到小)。要求使用两种方式排序,有一个 Book[] books=4 本书对象。使用传递实现 Comparator 接口匿名内部类(定制排序),完成:

  1. 按 price 从大到小
  2. 按 price 从小到大
  3. 按书名长度从大到小

代码实现

java
package com.hspedu.arrays_;

import java.util.Arrays;
import java.util.Comparator;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class ArrayExercise {
    public static void main(String[] args) {
        Book[] books = new Book[4];
        books[0] = new Book("红楼梦", 100);
        books[1] = new Book("金瓶梅新", 90);
        books[2] = new Book("青年文摘20年", 5);
        books[3] = new Book("java从入门到放弃~", 300);

        // (1)price 从大到小
        Arrays.sort(books, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Book book1 = (Book) o1;
                Book book2 = (Book) o2;
                double priceVal = book2.getPrice() - book1.getPrice();
                if (priceVal > 0) {
                    return 1;
                } else if (priceVal < 0) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });
        System.out.println("===按price从大到小===");
        System.out.println(Arrays.toString(books));

        // (2)price 从小到大
        Arrays.sort(books, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Book book1 = (Book) o1;
                Book book2 = (Book) o2;
                double priceVal = book2.getPrice() - book1.getPrice();
                if (priceVal > 0) {
                    return -1;
                } else if (priceVal < 0) {
                    return 1;
                } else {
                    return 0;
                }
            }
        });
        System.out.println("===按price从小到大===");
        System.out.println(Arrays.toString(books));

        // (3)按书名长度从大到小
        Arrays.sort(books, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Book book1 = (Book) o1;
                Book book2 = (Book) o2;
                // 按书名长度排序
                return book2.getName().length() - book1.getName().length();
            }
        });
        System.out.println("===按书名长度从大到小===");
        System.out.println(Arrays.toString(books));
    }
}

class Book {
    private String name;
    private double price;

    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

第 583 页

13.9 System 类

System 类常见方法和案例

方法说明
exit(int status)退出当前程序,0 表示正常退出
arraycopy(src, srcPos, dest, destPos, length)复制数组元素
currentTimeMillis()返回当前时间距离 1970-1-1 的毫秒数
gc()运行垃圾回收机制

System_.java

java
package com.hspedu.system_;

import java.util.Arrays;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class System_ {
    public static void main(String[] args) {
        // 1.exit 退出当前程序
        System.out.println("ok1");
        // System.exit(0);// 退出程序,后续代码不执行
        System.out.println("ok2");

        // 2.arraycopy 复制数组
        int[] src = {1, 2, 3};
        int[] dest = new int[3];// dest 当前是{0,0,0}
        /*
         * 参数说明:
         * src: 源数组
         * srcPos: 从源数组的哪个索引位置开始拷贝
         * dest: 目标数组
         * destPos: 把源数组的数据拷贝到目标数组的哪个索引
         * length: 从源数组拷贝多少个数据到目标数组
         */
        System.arraycopy(src, 0, dest, 0, src.length);
        System.out.println("dest=" + Arrays.toString(dest));// [1, 2, 3]

        // 3.currentTimeMillis 返回当前时间戳
        System.out.println(System.currentTimeMillis());
    }
}

第 585 页

13.10 BigInteger 和 BigDecimal 类

介绍

类名应用场景
BigInteger适合保存比较大的整型(long 不够用时)
BigDecimal适合保存精度更高的浮点型(double 不够用时)

常见方法

方法说明
add
subtract
multiply
divide除(BigDecimal 需指定精度)

BigInteger_.java

java
package com.hspedu.bignum;

import java.math.BigInteger;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class BigInteger_ {
    public static void main(String[] args) {
        // long 不够用的场景
        long l = 2378888889999999999L;
        System.out.println("l=" + l);

        BigInteger bigInteger = new BigInteger("23788888899999999999999999999");
        BigInteger bigInteger2 = new BigInteger("10099999999999999999999999999999999999999999999999999999999999999999999999999999999");

        // 加减乘除(不能直接用+、-、*、/)
        BigInteger add = bigInteger.add(bigInteger2);
        System.out.println("加=" + add);

        BigInteger subtract = bigInteger.subtract(bigInteger2);
        System.out.println("减=" + subtract);

        BigInteger multiply = bigInteger.multiply(bigInteger2);
        System.out.println("乘=" + multiply);

        BigInteger divide = bigInteger.divide(bigInteger2);
        System.out.println("除=" + divide);
    }
}

BigDecimal_.java

java
package com.hspedu.bignum;

import java.math.BigDecimal;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class BigDecimal_ {
    public static void main(String[] args) {
        // double 精度不够的场景
        double d = 1999.11111111111999999999999977788d;
        System.out.println(d);

        BigDecimal bigDecimal = new BigDecimal("1999.11");
        BigDecimal bigDecimal2 = new BigDecimal("3");

        // 加减乘
        System.out.println("加=" + bigDecimal.add(bigDecimal2));
        System.out.println("减=" + bigDecimal.subtract(bigDecimal2));
        System.out.println("乘=" + bigDecimal.multiply(bigDecimal2));

        // 除法(需指定精度,避免无限循环小数)
        System.out.println("除=" + bigDecimal.divide(bigDecimal2, BigDecimal.ROUND_CEILING));
    }
}

第 588 页

13.11 日期类

第一代日期类

核心类

  1. Date:精确到毫秒,代表特定的瞬间
  2. SimpleDateFormat:格式和解析日期的类,支持格式化(日期->文本)、解析(文本->日期)

格式字母表

字母日期或时间元素表示示例
GEra 标志符TextAD
yYear1996;96
M年中的月份MonthJuly; Jul; 07
w年中的周数Number27
W月份中的周数Number2
D年中的天数Number189
d月份中的天数Number10
F月份中的星期Number2
E星期中的天数TextTuesday; Tue
aAm/pm 标记TextPM
H一天中的小时数(0-23)Number0
k一天中的小时数(1-24)Number24
Kam/pm 中的小时数(0-11)Number0
ham/pm 中的小时数(1-12)Number12

Date01.java

java
package com.hspedu.date_;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class Date01 {
    public static void main(String[] args) throws ParseException {
        // 1. 获取当前系统时间
        Date d1 = new Date();
        System.out.println("d1=" + d1);

        // 2. 通过指定毫秒数得到时间
        Date d2 = new Date(9234567);
        System.out.println("d2=" + d2);

        // 3. 格式化日期
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
        String format = sdf.format(d1);
        System.out.println("当前日期=" + format);

        // 4. 解析文本为日期
        String s = "1996年01月01日10:20:30 星期一";
        Date parse = sdf.parse(s);
        System.out.println("parse=" + sdf.format(parse));
    }
}

第二代日期类:Calendar

基本介绍

Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。

Calendar_.java

java
package com.hspedu.date_;

import java.util.Calendar;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class Calendar_ {
    public static void main(String[] args) {
        // 1. 获取Calendar实例(抽象类,通过getInstance获取)
        Calendar c = Calendar.getInstance();

        // 2. 获取日历字段
        System.out.println("年:" + c.get(Calendar.YEAR));
        System.out.println("月:" + (c.get(Calendar.MONTH) + 1));// 月份从0开始,需+1
        System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
        System.out.println("小时(12小时制):" + c.get(Calendar.HOUR));
        System.out.println("小时(24小时制):" + c.get(Calendar.HOUR_OF_DAY));
        System.out.println("分钟:" + c.get(Calendar.MINUTE));
        System.out.println("秒:" + c.get(Calendar.SECOND));

        // 3. 自定义格式化输出
        System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" +
                c.get(Calendar.DAY_OF_MONTH) + " " + c.get(Calendar.HOUR_OF_DAY) + ":" +
                c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND));
    }
}

第 592 页

第三代日期类(JDK8+)

前两代日期类的不足

  1. 可变性:像日期和时间这样的类应该是不可变的
  2. 偏移性:Date 中的年份是从 1900 开始的,而月份都从 0 开始
  3. 格式化:格式化只对 Date 有用,Calendar 则不行
  4. 线程不安全,不能处理闰秒

核心类

类名说明
LocalDate只包含日期(年月日)
LocalTime只包含时间(时分秒)
LocalDateTime包含日期+时间(年月日时分秒)
DateTimeFormatter格式日期类(类似 SimpleDateFormat)
Instant时间戳(类似 Date)

LocalDate_.java

java
package com.hspedu.date_;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class LocalDate_ {
    public static void main(String[] args) {
        // 1. 获取当前日期时间
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println("当前日期时间=" + ldt);

        // 2. 格式化
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String format = dateTimeFormatter.format(ldt);
        System.out.println("格式化的日期=" + format);

        // 3. 获取单独字段
        System.out.println("年=" + ldt.getYear());
        System.out.println("月=" + ldt.getMonth());// 英文月份
        System.out.println("月=" + ldt.getMonthValue());// 数字月份
        System.out.println("日=" + ldt.getDayOfMonth());
        System.out.println("时=" + ldt.getHour());
        System.out.println("分=" + ldt.getMinute());
        System.out.println("秒=" + ldt.getSecond());

        // 4. 单独获取日期或时间
        LocalDate now = LocalDate.now();// 年月日
        LocalTime now2 = LocalTime.now();// 时分秒
        System.out.println("当前日期=" + now);
        System.out.println("当前时间=" + now2);

        // 5. 时间加减
        LocalDateTime localDateTime = ldt.plusDays(890);
        System.out.println("890 天后=" + dateTimeFormatter.format(localDateTime));

        LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
        System.out.println("3456 分钟前日期=" + dateTimeFormatter.format(localDateTime2));
    }
}

第 595 页

DateTimeFormatter 演示

java
// 自定义格式
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒");
LocalDateTime ldt = LocalDateTime.now();
String strDate = dtf.format(ldt);
System.out.println(strDate);

Instant 时间戳

java
package com.hspedu.date_;

import java.time.Instant;
import java.util.Date;

/**
 * @author 韩顺平
 * @version 1.0
 */
public class Instant_ {
    public static void main(String[] args) {
        // 1. 获取当前时间戳
        Instant now = Instant.now();
        System.out.println(now);

        // 2. Instant -> Date
        Date date = Date.from(now);

        // 3. Date -> Instant
        Instant instant = date.toInstant();
        System.out.println(instant);
    }
}

第三代日期类更多方法

  • MonthDay 类:检查重复事件
  • isLeapYear():判断是否是闰年
  • plusXxx():增加日期的某个部分
  • minusXxx():减少日期的某个部分

13.12 本章作业

编程题 Homework01.java

题目:将字符串中指定部分进行反转。比如将"abcdef"反转为"aedcbf" 要求:编写方法 public static String reverse(String str,int start,int end)搞定

编程题 Homework02.java(10min)

题目:输入用户名、密码、邮箱,如果信息录入正确,则提示注册成功,否则生成异常对象 要求

  1. 用户名长度为 2 或 3 或 4
  2. 密码的长度为 6,要求全是数字(isDigital)
  3. 邮箱中包含@和.并且@在.的前面

编程题 Homework03.java

题目

  1. 编写 java 程序,输入形式为:Han shun Ping 的人名,以 Ping, Han.S 的形式打印出来。其中.S 是中间单词的首字母。
  2. 例如输入"Willian Jefferson Clinton",输出形式为:Clinton, Willian.J

编程题 Homework04.java(5min)

题目:输入字符串,判断里面有多少个大写字母,多少个小写字母,多少个数字